﻿using Autofac;
using Autofac.Core;
using DotNetExtensions.Core.IOC;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Builder = Autofac.ContainerBuilder;

namespace DotNetExtensions.Core.Autofac
{
    public class RegistTypeResolver : Singleton<RegistTypeResolver>
    {

        private RegistTypeResolver()
        {
            registTypes = new ConcurrentDictionary<Type, List<Type>>();
        }

        private readonly ConcurrentDictionary<Type, List<Type>> registTypes;
        public ConcurrentDictionary<Type, List<Type>> RegistTypes => registTypes;

        public Builder Builder { get; set; }

        public ValueCollection<Assembly> Assemblies { get; set; }

        public static bool IsRegistered(Type type)
        {
            return Default.LifetimeScope != null && Default.LifetimeScope.IsRegistered(type);
        }
        public ILifetimeScope LifetimeScope { get; set; }
        public static T Resolve<T>()
        {
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegistered(typeof(T))) throw new Exception($"Type [{typeof(T)}] not registed");
            return Default.LifetimeScope.Resolve<T>();
        }
        public static object Resolve(Type type)
        {
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegistered(type)) throw new Exception($"Type [{type.FullName}] not registed");
            return Default.LifetimeScope.Resolve(type);
        }
        public static object Resolve(Type type, params Parameter[] parameters)
        {
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegistered(type)) throw new Exception($"Type [{type.FullName}] not registed");
            return Default.LifetimeScope.Resolve(type, parameters);
        }
        public static object ResolveNamed(string named, Type type)
        {
            named = type.Name + "-" + named;
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegisteredWithName(named, type)) throw new Exception($"Named [{named}] with type [{type.FullName}] not registed");

            return Default.LifetimeScope.ResolveNamed(named, type);
        }
        public static object ResolveNamed(string named, Type type, params Parameter[] parameters)
        {
            named = type.Name + "-" + named;
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegisteredWithName(named, type)) throw new Exception($"Named [{named}] with type [{type.FullName}] not registed");

            return Default.LifetimeScope.ResolveNamed(named, type, parameters);
        }
        public static T ResolveNamed<T>(string named)
        {
            named = typeof(T).Name + "-" + named;
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegisteredWithName(named, typeof(T))) throw new Exception($"Named [{named}] with type [{typeof(T).FullName}] not registed");

            return (T)Default.LifetimeScope.ResolveNamed(named, typeof(T));
        }

        public static T ResolveNamed<T>(string named, params Parameter[] parameters)
        {
            named = typeof(T).Name + "-" + named;
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegisteredWithName(named, typeof(T))) throw new Exception($"Named [{named}] with type [{typeof(T).FullName}] not registed");

            return (T)Default.LifetimeScope.ResolveNamed(named, typeof(T), parameters);
        }

        public static T Resolve<T>(params Parameter[] parameters)
        {
            if (Default.LifetimeScope == null || !Default.LifetimeScope.IsRegistered(typeof(T))) throw new Exception($"Type [{typeof(T)}] not registed");
            return Default.LifetimeScope.Resolve<T>(parameters);
        }

        public static List<T> ResolveNamed<T>()
        {
            if (Default.RegistTypes.TryGetValue(typeof(T), out List<Type> types))
            {
                List<T> resolved = new List<T>();
                foreach (var type in types)
                {
                    var @inject = type.GetCustomAttribute<InjectAttribute>();
                    if (@inject == null) @inject = new InjectAttribute();

                    string named = inject.Named ?? type.FullName;
                    resolved.Add(ResolveNamed<T>(named));
                }

                return resolved;
            }

            return default(List<T>);
        }

        public static List<Type> GetMarks<T>()
        {
            if (Default.RegistTypes.TryGetValue(typeof(T), out List<Type> types))
            {
                return types;
            }

            return new List<Type>();
        }
    }
}
